package com.prawn.authority.service;

import java.util.*;
import java.util.concurrent.TimeUnit;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import com.prawn.authority.client.traceabilityClient;
import com.prawn.authority.dao.FunctionDao;
import com.prawn.authority.dao.RoleDao;
import com.prawn.authority.pojo.*;
import com.prawn.authority.pojo.vo.InvitationVo;
import com.prawn.authority.utils.GlobalException;
import com.prawn.authority.utils.SecurityContextUtil;
import entity.Base;
import entity.Result;
import entity.StatusCode;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import util.IdWorker;

import com.prawn.authority.dao.UserDao;

/**
 * 服务层
 *
 * @author Administrator
 */
@Service
public class UserService {

	@Autowired
	private UserDao userDao;

	@Autowired
	private IdWorker idWorker;

	@Autowired
	private BCryptPasswordEncoder encoder;

	@Autowired
	private RoleDao roleDao;

	@Autowired
	private AuthenticationManager authenticationManager;

	@Autowired
	private FunctionDao functionDao;

	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private traceabilityClient traceabilityClient;

	@Value("${user.ttl}")
	private int ttl;

	@Value("${user.passwordErrors}")
	private int passwordErrors;

	@Value("${invitation.cool-down}")
	private int invitationCoolDown;


	/**
	 * 注册
	 *
	 * @param user
	 */
	public void register(User user) {

		try {
			user.setUserId(idWorker.nextId() + "");
			// 加密
			user.setPassword(encoder.encode(user.getPassword()));
			user.setIsAdmin(0);
			user.setCreateDate(new Date());
			userDao.save(user);
		} catch (DataIntegrityViolationException e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "用户已存在"));
		} catch (Exception e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "注册失败"));
		}
	}


	/**
	 * 登陆
	 *
	 * @param user
	 * @return
	 */
	public User login(User user) {
		UserDetailsImpl userDetailsImpl = null;
		// 用户所拥有所有权限的集合（用到set集合的目的，是为了去掉重复的权限）
		Set<GrantedAuthority> authorities = new HashSet<>();

		// 通过用户登陆的用户名，从数据库查出对应用户名的用户
		User userLogin = userDao.findByLoginId(user.getLoginId());

		if (userLogin == null) {
			throw new GlobalException(new Result(false, StatusCode.LOGINERROR, "用户名不存在"));
		}


		// 用BCryptPasswordEncoder对密码进行匹配
		if (!encoder.matches(user.getPassword(), userLogin.getPassword())) {
			// 密码匹配不成功，即密码错误错误
			// 通过'用户名_passwordErrors'，从redis中取出该用户密码已经错误的次数
			Object num = redisTemplate.opsForValue().get(userLogin.getUserId() + "_passwordErrors");
			// 如果是第一次错误，此时redis中还没有相应的'key-value'
			if (num == null) {
				// 第一次错误，redis中还没有相应的key-value，插入值为1
				redisTemplate.opsForValue().set(userLogin.getUserId() + "_passwordErrors", 1, ttl, TimeUnit.MINUTES);
			} else {
				// 不是第一次，直接把value值+1，再放入到redis中
				redisTemplate.opsForValue().set(userLogin.getUserId() + "_passwordErrors", (int) num + 1, ttl, TimeUnit.MINUTES);
				// 超出最大错误次数，直接抛出异常，并禁止用户继续操作
				if ((int) num > passwordErrors) {
					throw new GlobalException(new Result(false, StatusCode.LOGINERROR, "密码错误次数过多，请" + ttl + "分钟后重试"));
				}
			}
		}
		//如果用户baseId不为空且基地身份不为2（不为老板）则到authority_base_role_function读取用户权限
		if (userLogin.getBaseId() != null && userLogin.getBaseIdentity() > 2) {
			List<Function> functionList = functionDao.findFunctionByBase(userLogin.getBaseId(), userLogin.getBaseIdentity());
			for (Function function : functionList) {
				authorities.add(new SimpleGrantedAuthority(function.getName()));
			}
		} else {
			// 通过用户的Id，查询该用户拥有的所有角色
			List<Role> roleList = roleDao.findByUserId(userLogin.getUserId());
			for (Role role : roleList) {
				//查询操作权限
				List<Function> functionList = functionDao.findByRoleId(role.getId());
				for (Function function : functionList) {
					authorities.add(new SimpleGrantedAuthority(function.getName()));
				}
			}
		}
		// security的认证判断是通过authenticationManager去判断构造的UsernamePasswordToken
		// 然后生成验证通过后Authentication，将这个Authentication放入SecurityContextHolder中即可实现认证。
		// 所以在这里直接注入authenticationManager
		UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
				new UsernamePasswordAuthenticationToken(user.getLoginId(), user.getPassword(), authorities);
		Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
		// 将认证放入安全上下文中
		SecurityContextUtil.setAuthentication(authentication);

		// 此时的UserDetails实例不带有权限信息
		userDetailsImpl = (UserDetailsImpl) authentication.getPrincipal();
		// 将权限信息设置到UserDetails实例中
		userDetailsImpl.setAuthorities(usernamePasswordAuthenticationToken.getAuthorities());
		// 这个对象返回给controller去生成jwt
		return userDetailsImpl;
	}


	/**
	 * 查询全部用户列表
	 *
	 * @return
	 */
	public List<User> findAllUser() {
		return userDao.findAllByIsAdmin(0);
	}

	/**
	 * 查询全部普通管理员列表
	 *
	 * @return
	 */
	public List<User> findAllAdmin() {
		List<User> list = userDao.findAllByIsAdmin(1);
		list.addAll(userDao.findAllByIsAdmin(2));
		return list;
	}


	/**
	 * 条件查询+分页用户
	 *
	 * @param whereMap
	 * @param page
	 * @param size
	 * @return
	 */
	public Page<User> findSearchUser(Map whereMap, int page, int size, String isAdmin) {
		whereMap.put("isAdmin", isAdmin);
		Specification<User> specification = createSpecification(whereMap);
		PageRequest pageRequest = PageRequest.of(page - 1, size);
		return userDao.findAll(specification, pageRequest);
	}


	/**
	 * 条件查询用户
	 *
	 * @param whereMap
	 * @return
	 */
	public List<User> findSearchUser(Map whereMap, String isAdmin) {
		whereMap.put("isAdmin", isAdmin);
		Specification<User> specification = createSpecification(whereMap);
		return userDao.findAll(specification);
	}

	/**
	 * 根据ID查询普通用户
	 *
	 * @param userId
	 * @return
	 */
	public User findUserByUserId(String userId) {
		User user = userDao.findByUserIdAndIsAdmin(userId, 0);
		// 若为空就抛出错误
		if (user == null || "".equals(userId)) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "用户不存在"));
		}
		return user;
	}

	/**
	 * 根据ID查询管理员
	 *
	 * @param userId
	 * @return
	 */
	public User findAdminByUserId(String userId) {
		User user = userDao.findByUserIdAndIsAdmin(userId, 1);
		User user2 = userDao.findByUserIdAndIsAdmin(userId, 2);
		// 若为空就抛出错误
		if (user != null && !"".equals(user)) {
			return user;
		} else if (user2 != null && !"".equals(user2)) {
			return user2;
		} else throw new GlobalException(new Result(false, StatusCode.ERROR, "用户不存在"));
	}

	/**
	 * 根据LoginID查询
	 *
	 * @param loginId
	 * @return
	 */
	public User findByLoginId(String loginId) {
		User user = userDao.findByLoginId(loginId);
		if (user == null) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "用户不存在"));
		}
		return user;
	}

	/**
	 * 增加普通用户
	 *
	 * @param user
	 */
	public void add(User user) {
		try {
			user.setUserId(idWorker.nextId() + "");
			// 加密
			user.setPassword(encoder.encode(user.getPassword()));
			user.setCreateDate(new Date());
			userDao.save(user);
		} catch (DataIntegrityViolationException e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "用户已存在"));
		} catch (Exception e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "添加失败"));
		}
	}


	/**
	 * 修改
	 *
	 * @param user
	 */
	@Transactional
	public void updateUser(User user, int isAdmin) {
		User userLogin;
		if (isAdmin == 0) {
			userLogin = findUserByUserId(user.getUserId());
		} else {
			userLogin = findAdminByUserId(user.getUserId());
		}
		if (userLogin == null) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "不存在"));
		} else {
			try {
				user.setIsAdmin(user.getIsAdmin() == null ? userLogin.getIsAdmin() : user.getIsAdmin());
				user.setLoginId(user.getLoginId() == null ? userLogin.getLoginId() : user.getLoginId());
				user.setName(user.getName() == null ? userLogin.getName() : user.getName());
				user.setEmail(user.getEmail() == null ? userLogin.getEmail() : user.getEmail());
				user.setPhone(user.getPhone() == null ? userLogin.getPhone() : user.getPhone());
				user.setMobile(user.getMobile() == null ? userLogin.getMobile() : user.getMobile());
				user.setSex(user.getSex() == null ? userLogin.getSex() : user.getSex());
				user.setMerchantId(user.getMerchantId() == null ? userLogin.getMerchantId() : user.getMerchantId());
				user.setBaseId(user.getBaseId() == null ? userLogin.getBaseId() : user.getBaseId());
				user.setBaseIdentity(user.getBaseIdentity() == null ? userLogin.getBaseIdentity() : user.getBaseIdentity());

				// 密码加密
				user.setPassword(user.getPassword() == null ? userLogin.getPassword() : encoder.encode(user.getPassword()));
				userDao.save(user);
			} catch (Exception e) {
				throw new GlobalException(new Result(false, StatusCode.ERROR, "修改失败"));
			}
		}
	}

	/**
	 * 删除用户
	 *
	 * @param id
	 */
	@Transactional
	public void deleteUserById(String id, int isAdmin) {
		try {
			userDao.deleteByUserIdAndIsAdmin(id, isAdmin);
		} catch (EmptyResultDataAccessException e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "不存在"));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "删除失败"));
		}
	}

	/**
	 * 动态条件构建
	 *
	 * @param searchMap
	 * @return
	 */
	private Specification<User> createSpecification(Map searchMap) {

		return new Specification<User>() {

			@Override
			public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> predicateList = new ArrayList<Predicate>();// 用户编号if (searchMap.get("userId")!=null && !"".equals(searchMap.get("userId"))) {predicateList.add(cb.like(root.get("userId").as(String.class), "%"+(String)searchMap.get("userId")+"%")); }// 是否管理员if (searchMap.get("isAdmin")!=null && !"".equals(searchMap.get("isAdmin"))) {predicateList.add(cb.equal(root.get("isAdmin"), searchMap.get("isAdmin"))); }// 用户登录账号if (searchMap.get("loginId")!=null && !"".equals(searchMap.get("loginId"))) {predicateList.add(cb.like(root.get("loginId").as(String.class), "%"+(String)searchMap.get("loginId")+"%")); }
				// 用户id
				if (!StringUtils.isEmpty(searchMap.get("userId"))) {
					predicateList.add(cb.like(root.get("userId").as(String.class), "%" + (String) searchMap.get("userId") + "%"));
				}
				// 用户loginId
				if (!StringUtils.isEmpty(searchMap.get("loginId"))) {
					predicateList.add(cb.like(root.get("loginId").as(String.class), "%" + (String) searchMap.get("loginId") + "%"));
				}
				// isAdmin
				if (!StringUtils.isEmpty(searchMap.get("isAdmin"))) {
					if ("0".equals(searchMap.get("isAdmin"))) {
						predicateList.add(cb.equal(root.get("isAdmin"), searchMap.get("isAdmin")));
					} else if ("1".equals(searchMap.get("isAdmin"))) {
						predicateList.add(cb.or(cb.equal(root.get("isAdmin"), 2), cb.equal(root.get("isAdmin"), 1)));
					}
				}
				// 姓名
				if (!StringUtils.isEmpty(searchMap.get("name"))) {
					predicateList.add(cb.like(root.get("name").as(String.class), "%" + (String) searchMap.get("name") + "%"));
				}
				// 邮箱
				if (!StringUtils.isEmpty(searchMap.get("email"))) {
					predicateList.add(cb.like(root.get("email").as(String.class), "%" + (String) searchMap.get("email") + "%"));
				}
				// 电话
				if (!StringUtils.isEmpty(searchMap.get("phone"))) {
					predicateList.add(cb.like(root.get("phone").as(String.class), "%" + (String) searchMap.get("phone") + "%"));
				}
				// 手机
				if (!StringUtils.isEmpty(searchMap.get("mobile"))) {
					predicateList.add(cb.like(root.get("mobile").as(String.class), "%" + (String) searchMap.get("mobile") + "%"));
				}
				// 性别
				if (!StringUtils.isEmpty(searchMap.get("sex"))) {
					predicateList.add(cb.like(root.get("sex").as(String.class), "%" + (String) searchMap.get("sex") + "%"));
				}
				// 店铺Id
				if (!StringUtils.isEmpty(searchMap.get("merchantId"))) {
					predicateList.add(cb.like(root.get("merchantId").as(String.class), "%" + (String) searchMap.get("merchantId") + "%"));
				}
				// 基地Id
				if (!StringUtils.isEmpty(searchMap.get("baseId"))) {
					predicateList.add(cb.like(root.get("baseId").as(String.class), "%" + (String) searchMap.get("baseId") + "%"));
				}
				// 是否基地管理员
				if (!StringUtils.isEmpty(searchMap.get("baseIdentity"))) {
					predicateList.add(cb.equal(root.get("baseIdentity"), searchMap.get("baseIdentity")));
				}

				return cb.and(predicateList.toArray(new Predicate[predicateList.size()]));
			}
		};

	}

	/**
	 * 更新管理员角色
	 *
	 * @param userId
	 * @param RoleIdList
	 */
	@Transactional
	public void UpdateRoleOfAdmin(String userId, Map RoleIdList) {
		try {
			// 查询用户
			User user = findAdminByUserId(userId);
			if (user == null) {
				throw new RuntimeException("管理员不存在");
			}

			// 删除用户的所有角色
			userDao.deleteALLById(userId);

			List<Object> roleList = (List<Object>) RoleIdList.get("roleIdList");
			for (Object roleId : roleList) {
				// 查询角色
				// id不为空但查询结果为空
				if (!roleDao.findById((String) roleId).isPresent() && roleId != null) {
					throw new RuntimeException(roleId + "角色不存在");
				}
				// 添加权限
				userDao.addRoleToUser(userId, (String) roleId);
			}
		} catch (Exception e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "给管理员添加角色失败," + e.getMessage()));
		}
	}


	/**
	 * 申请基地管理员
	 *
	 * @param userId
	 * @param baseId
	 * @param baseIdentity
	 */
	public void applyForBaseAdmin(String userId, String baseId, int baseIdentity) {
		try {
			// 查找用户是否存在
			findUserByUserId(userId);
			// 可能有未处理的申请
			// 先读取，加入新的，再重新加回去
			Map<String, Map> message = (Map) redisTemplate.opsForValue().get("baseMessage");
			// redis中没有请求信息
			if (message == null) {
				message = new HashMap<>();
			}

			Map<String, String> map = new HashMap<>();
			map.put("baseId", baseId);
			map.put("baseIdentity", String.valueOf(baseIdentity));

			message.put(userId, map);

			redisTemplate.opsForValue().set("baseMessage", message);
		} catch (GlobalException e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "申请失败"));
		}
	}

	/**
	 * 申请基地管理员的状态
	 *
	 * @param userId
	 * @return
	 */
	public Map<String, String> applyForStatus(String userId) {
		Map<String, String> map = null;
		Map<String, Map> message = (Map) redisTemplate.opsForValue().get("baseMessage");
		if (message != null) {
			map = message.get(userId);
		}

		return map;
	}

	/**
	 * 更新基地管理员
	 *
	 * @param userId
	 * @param baseId
	 * @param baseIdentity
	 */
	@Transactional
	public void updateBaseAdmin(String userId, String baseId, int baseIdentity) {
		try {
			User user = findUserByUserId(userId);
			user.setBaseId(baseId);
			user.setBaseIdentity(baseIdentity);
			updateUser(user, 0);
		} catch (GlobalException e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "授权失败"));
		}
	}

	/**
	 * 申请新建基地
	 *
	 * @param userId
	 * @param base
	 */
	public void applyForAddBase(String userId, Base base) {
		try {
			// 查找用户是否存在
			findUserByUserId(userId);
			// 可能有未处理的申请
			// 先读取，加入新的，再重新加回去
			Map<String, Base> message = (Map) redisTemplate.opsForValue().get("addBaseMessage");
			// redis中没有请求信息
			if (message == null) {
				message = new HashMap<>();
			}

			message.put(userId, base);

			redisTemplate.opsForValue().set("addBaseMessage", message);
		} catch (GlobalException e) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "申请失败"));
		}
	}

	/**
	 * 申请新建基地的状态
	 *
	 * @param userId
	 * @return
	 */
	public Base applyForAddBaseStatus(String userId) {
		Base base = null;
		Map<String, Base> message = (Map) redisTemplate.opsForValue().get("addBaseMessage");
		if (message != null) {
			base = message.get(userId);
		}

		return base;
	}

	/**
	 * 上传头像
	 */
	@Transactional
	public void uploadPhoto(String userId, String photoUrl) {
		try {
			userDao.addPhoto(userId, photoUrl);
		} catch (GlobalException e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "上传失败"));
		}
	}

	/**
	 * 修改头像
	 */
	@Transactional
	public void updatePhoto(String userId, String photoUrl) {
		try {
			userDao.updatePhoto(userId, photoUrl);
		} catch (GlobalException e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "修改失败"));
		}
	}

	/**
	 * 删除头像(置空)
	 */
	@Transactional
	public void delPhoto(String userId) {
		try {
			userDao.delPhoto(userId);
		} catch (GlobalException e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "删除失败"));
		}
	}

	/**
	 * 删除头像(删除账号把头像一起删除)
	 */
	@Transactional
	public void delUserAndPhoto(String userId) {
		try {
			userDao.delUserAndPhoto(userId);
		} catch (GlobalException e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "删除失败"));
		}
	}

	/**
	 * 获取头像
	 */
	public String getPhoto(String userId) {
		String photoUrl = null;
		try {
			photoUrl = userDao.getPhoto(userId);
			return photoUrl;
		} catch (GlobalException e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "获取头像失败"));
		}
	}

	/**
	 * 查询基地信息
	 *
	 * @param userId 用户id
	 * @return 返回基地id和改用户在基地的身份
	 */
	public HashMap<String, String> getBase(String userId) {
		try {
			HashMap<String, String> map = new HashMap<>();
			String base = userDao.getBase(userId);
			String[] split = base.split(",");
			if (split[0] != null && !"".equals(split[0])) {
				map.put("baseId", split[0]);
				map.put("baseIdentity", split[1]);
			} else return null;
			return map;
		} catch (GlobalException e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "获取基地信息失败"));
		}

	}

	/**
	 * 查询店铺信息
	 *
	 * @param userId 用户id
	 * @return 返回店铺信息的id
	 */
	public HashMap<String, String> getMerchant(String userId) {
		try {
			HashMap<String, String> map = new HashMap<>();
			String merchantId = userDao.getMerchant(userId);
			if (merchantId != null && !"".equals(merchantId)) {
				map.put("merchantId", merchantId);
			} else return null;
			return map;
		} catch (GlobalException e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "获取店铺失败"));
		}
	}

	/**
	 * 修改用户的基地Id
	 *
	 * @param userId
	 * @param baseId
	 * @param identity
	 */
	@Transactional
	public void setBase(String userId, String baseId, Integer identity) {
		userDao.setBase(baseId, identity, userId);
		userDao.addRoleToUser(userId, "1320344284431519744");
	}

	/**
	 * 删除基地的时候修改用户的基地信息为空
	 *
	 * @param baseId
	 * @return
	 */
	@Transactional
	public Result delBase(String baseId) {
		userDao.setBaseNull(baseId);
		return traceabilityClient.delBase(baseId);
	}

	/**
	 * 根据基地Id获取用户
	 *
	 * @param BaseId
	 * @return
	 */
	public ArrayList<User> getUserByBaseId(String BaseId) {
		try {
			ArrayList<User> users = userDao.getUsersByBaseId(BaseId);
			for (User user : users) {
				user.setPassword(null);
			}
			return users;
		} catch (GlobalException e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "获取用户信息失败"));
		}
	}

	/**
	 * 获取所有没有进基地的用户
	 *
	 * @return
	 */
	public List<User> listNoBaseUser() {
		ArrayList<User> userList = userDao.getUsersByBaseId(null);
		userList.stream().forEach(user -> user.setPassword(null));
		return userList;
	}

	/**
	 * 带条件查询未进基地的用户
	 * @param searchMap
	 * @return
	 */
	public List<User> listNoBaseUser(Map<String, String> searchMap) {
		Specification<User> specification = createBaseSpecification(searchMap);
		return userDao.findAll(specification);
	}

	/**
	 * 分页+条件查询没有进基地的用户
	 *
	 * @param searchMap
	 * @param page
	 * @param size
	 * @return
	 */
	public Page<User> pageListNoBaseUser(Map searchMap, int page, int size) {
		Specification<User> specification = createBaseSpecification(searchMap);
		PageRequest pageRequest = PageRequest.of(page - 1, size);
		Page<User> userPage = userDao.findAll(specification, pageRequest);
		userPage.forEach(user -> user.setPassword(null));
		return userPage;
	}

	Specification<User> createBaseSpecification(Map searchMap) {
		return new Specification<User>() {
			@Override
			public Predicate toPredicate(Root<User> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
				List<Predicate> predicateList = new ArrayList<Predicate>();// 用户编号if (searchMap.get("userId")!=null && !"".equals(searchMap.get("userId"))) {predicateList.add(cb.like(root.get("userId").as(String.class), "%"+(String)searchMap.get("userId")+"%")); }// 是否管理员if (searchMap.get("isAdmin")!=null && !"".equals(searchMap.get("isAdmin"))) {predicateList.add(cb.equal(root.get("isAdmin"), searchMap.get("isAdmin"))); }// 用户登录账号if (searchMap.get("loginId")!=null && !"".equals(searchMap.get("loginId"))) {predicateList.add(cb.like(root.get("loginId").as(String.class), "%"+(String)searchMap.get("loginId")+"%")); }
				// 用户loginId
				if (!StringUtils.isEmpty(searchMap.get("loginId"))) {
					predicateList.add(cb.like(root.get("loginId").as(String.class), "%" + (String) searchMap.get("loginId") + "%"));
				}
				// 姓名
				if (!StringUtils.isEmpty(searchMap.get("name"))) {
					predicateList.add(cb.like(root.get("name").as(String.class), "%" + (String) searchMap.get("name") + "%"));
				}
				// 邮箱
				if (!StringUtils.isEmpty(searchMap.get("email"))) {
					predicateList.add(cb.like(root.get("email").as(String.class), "%" + (String) searchMap.get("email") + "%"));
				}
				// 电话
				if (!StringUtils.isEmpty(searchMap.get("phone"))) {
					predicateList.add(cb.like(root.get("phone").as(String.class), "%" + (String) searchMap.get("phone") + "%"));
				}
				// 手机
				if (!StringUtils.isEmpty(searchMap.get("mobile"))) {
					predicateList.add(cb.like(root.get("mobile").as(String.class), "%" + (String) searchMap.get("mobile") + "%"));
				}
				// 性别
				if (!StringUtils.isEmpty(searchMap.get("sex"))) {
					predicateList.add(cb.like(root.get("sex").as(String.class), "%" + (String) searchMap.get("sex") + "%"));
				}
				// 店铺Id
				if (!StringUtils.isEmpty(searchMap.get("merchantId"))) {
					predicateList.add(cb.like(root.get("merchantId").as(String.class), "%" + (String) searchMap.get("merchantId") + "%"));
				}
				// 基地Id为空
				predicateList.add(cb.isNull(root.get("baseId").as(String.class)));
				return cb.and(predicateList.toArray(new Predicate[predicateList.size()]));
			}
		};
	}

	/**
	 * 邀请用户进基地
	 *
	 * @param userId
	 * @param baseId
	 */
	public void inviteUserIntoBase(String userId, String baseId) {
		// 查询用户是否存在
		User user = userDao.findByUserId(userId);
		if (user == null) {
			// 用户不存在
			throw new GlobalException(Result.error(StatusCode.ERROR, "用户不存在"));
		}
		if (!StringUtils.isEmpty(user.getBaseId())) {
			// 用户已有自己的基地
			throw new GlobalException(Result.error(StatusCode.ERROR, "该用户已有基地"));
		}

		// 保存基地对用户的邀请
		LinkedList<String> baseIdList = (LinkedList<String>) redisTemplate.opsForHash().get("InvitationMessage", userId);
		// 保存基地的邀请记录,存在三种状态等待(2)，同意(1)，拒绝(0)
		Map<String, LinkedList<String>> invitationRecord = (Map<String, LinkedList<String>>) redisTemplate.opsForHash().get("InvitationRecord", baseId);
		// 第一次邀请，redis不存在该对象
		if (invitationRecord == null) {
			invitationRecord = new HashMap<String, LinkedList<String>>();
			invitationRecord.put("0", new LinkedList<String>());
			invitationRecord.put("1", new LinkedList<String>());
			invitationRecord.put("2", new LinkedList<String>());
		}
		LinkedList<String> waitingInvitationRecord = invitationRecord.get("2");

		if (baseIdList == null) {
			// 没有基地邀请该用户
			baseIdList = new LinkedList<>();
		} else {
			// 判断是否已经被该基地邀请
			if (baseIdList.contains(baseId)) {
				// 该基地已对该用户发过邀请
				// 检测冷却时间是否已过
				Object o = redisTemplate.opsForValue().get("InvitationCool_" + baseId + userId);
				if (o != null) {
					// 冷却时间还未过
					throw new GlobalException(Result.error(StatusCode.DUPLICATE, "邀请过于繁忙，请明天再来"));
				}
				// 冷却时间过了，InvitationMessage中对该基地的记录，删除waitingInvitationRecord的该用户发送邀请的记录
				baseIdList.remove(baseId);
				waitingInvitationRecord.remove(baseId);
			}
		}

		// 发送邀请,UserId作为redis的key，linkedList作为value（linkedList头部放入baseId）
		baseIdList.addFirst(baseId);
		redisTemplate.opsForHash().put("InvitationMessage", userId, baseIdList);
		// 基地保存邀请记录
		waitingInvitationRecord.addFirst(userId);
		redisTemplate.opsForHash().put("InvitationRecord", baseId, invitationRecord);
		// 以'InvitationCool_ baseId+userId'作为key，value值设置为1,过期时间为冷却时间
		redisTemplate.opsForValue().set("InvitationCool_" + baseId + userId, 1, invitationCoolDown, TimeUnit.SECONDS);
	}

	/**
	 * 用户查询目前被哪些基地邀请
	 *
	 * @param userId
	 * @return
	 */
	public List<Map<String, String>> findAllInvitation(String userId) {
		LinkedList<String> baseIdList = (LinkedList<String>) redisTemplate.opsForHash().get("InvitationMessage", userId);
		// 判断baseIdList是否为空
		if (baseIdList == null) {
			// baseIdList为空，表示没有基地邀请该用户
			return null;
		}
		// baseIdList不为空
		// 创建一个存储base的List集合
		List<Map<String, String>> baseInfoList = new ArrayList<Map<String, String>>();
		// 通过baseIdList存储的baseId调用远程服务来获取每个base
		for (String baseId : baseIdList) {
			Map<String, String> baseInfo = (Map<String, String>) traceabilityClient.getBase(baseId).getData();
			if (baseInfo != null) {
				baseInfoList.add(baseInfo);
			}
		}
		return baseInfoList;
	}

	/**
	 * 用户选择选择同意（拒绝）进入基地
	 *
	 * @param userId
	 * @param baseId
	 * @param selection
	 */
	@Transactional
	public void executeSelection(String userId, String baseId, String selection) {
		// 判断该baseId的基地是否存在
		if (traceabilityClient.getBase(baseId).getData() == null) {
			//基地不存在
			throw new GlobalException(Result.error(StatusCode.ERROR, "传入的baseId有误"));
		}
		// 获取通过redis获取邀请该用户的基地ID
		LinkedList<String> baseIdList = (LinkedList<String>) redisTemplate.opsForHash().get("InvitationMessage", userId);
		if (baseIdList == null) {
			// 主要是防止有基地的用户直接非法操作进到该接口,message可随便填写
			throw new GlobalException(Result.error(StatusCode.ERROR, "邀请已过期"));
		}
		// 判断是否该baseId基地有邀请该用户
		if (!baseIdList.contains(baseId)) {
			// 该baseId基地没有邀请该用户
			throw new GlobalException(Result.error(StatusCode.ERROR, "传入的baseId有误"));
		}

		// 判断用户选择了哪一项
		if (selection.equals("1")) {
			// 选择了同意
			for (String baseIdStr : baseIdList) {
				// 删除所有基地的对该用户的等待记录
				Map<String, LinkedList<String>> invitationRecord = (Map<String, LinkedList<String>>) redisTemplate.opsForHash().get("InvitationRecord", baseIdStr);
				invitationRecord.get("2").remove(userId);
				// 删除所有基地对该该用户邀请对冷却时间限制
				redisTemplate.delete("InvitationCool_" + baseIdStr + userId);
				// 判断当前遍历到的基地是否为用户同意进入的基地
				if (baseId.equals(baseIdStr)) {
					// 该基地为用户同意进入的基地
					// 该基地的同意日志进行记录
					invitationRecord.get("1").addFirst(userId);
				} else {
					// 该基地不是用户同意进入的基地
					// 该基地的拒绝日志进行记录
					invitationRecord.get("0").addFirst(userId);
				}
				redisTemplate.opsForHash().put("InvitationRecord", baseIdStr, invitationRecord);
			}
			// 删除该用户的所有基地邀请
			redisTemplate.opsForHash().delete("InvitationMessage", userId);
			// 修改用户的信息（添加上baseId字段和baseIdentity字段）,初始化身份是员工
			userDao.setBase(baseId, 4, userId);
		} else {
			// 选择了拒绝
			// 对基地邀请人记录进行修改，等待记录中删除该用户，拒绝记录增加该用户
			Map<String, LinkedList<String>> invitationRecord = (Map<String, LinkedList<String>>) redisTemplate.opsForHash().get("InvitationRecord", baseId);
			invitationRecord.get("2").remove(userId);
			invitationRecord.get("0").addFirst(userId);
			redisTemplate.opsForHash().put("InvitationRecord", baseId, invitationRecord);
			// 删除该用户该基地的邀请
			baseIdList.remove(baseId);
			redisTemplate.opsForHash().put("InvitationMessage", userId, baseIdList);
		}
	}

	/**
	 * 基地获取邀请用户记录
	 *
	 * @param baseId
	 * @return
	 */
	public List<InvitationVo> getInvitationRecord(String baseId) {
		// 判断基地是否存在
		if (StringUtils.isEmpty(baseId) || traceabilityClient.getBase(baseId).getData() == null) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "基地不存在"));
		}
		// 获取基地所有邀请记录
		Map<String, LinkedList<String>> invitationRecord = (Map<String, LinkedList<String>>) redisTemplate.opsForHash().get("InvitationRecord", baseId);
		if (invitationRecord == null) {
			// 该基地没有邀请过人
			return null;
		}

		// 存放三种状态记录的Map
		Map<String, List<User>> map = new HashMap<String, List<User>>();
		LinkedList<InvitationVo> invitationVos = new LinkedList<InvitationVo>();

		// 等待记录
		LinkedList<String> waitingLinkedList = invitationRecord.get("2");
		// 同意记录
		LinkedList<String> acceptedLinkedList = invitationRecord.get("1");
		// 拒绝记录
		LinkedList<String> refusedLinkedList = invitationRecord.get("0");


		// 查库，进行组装
		if (waitingLinkedList != null && waitingLinkedList.size() != 0) {
			userDao.findAllById(waitingLinkedList).stream().forEach(user -> {
				InvitationVo invitationVo = new InvitationVo();
				BeanUtils.copyProperties(user, invitationVo);
				invitationVo.setStatus("2");
				invitationVo.setPassword(null);
				invitationVos.add(invitationVo);
			});
		}

		if (acceptedLinkedList != null && acceptedLinkedList.size() != 0) {
			userDao.findAllById(acceptedLinkedList).stream().forEach(user -> {
				InvitationVo invitationVo = new InvitationVo();
				BeanUtils.copyProperties(user, invitationVo);
				invitationVo.setStatus("1");
				invitationVo.setPassword(null);
				invitationVos.add(invitationVo);
			});
		}

		if (refusedLinkedList != null && refusedLinkedList.size() != 0) {
			userDao.findAllById(refusedLinkedList).stream().forEach(user -> {
				InvitationVo invitationVo = new InvitationVo();
				BeanUtils.copyProperties(user, invitationVo);
				invitationVo.setStatus("0");
				invitationVo.setPassword(null);
				invitationVos.add(invitationVo);
			});
		}
		return invitationVos;
	}

	/**
	 * 基地获取指定类型的邀请记录
	 *
	 * @param baseId
	 * @param typeId
	 * @return
	 */
	public List<User> getTypeInvitationRecord(String baseId, String typeId) {
		// 判断基地是否存在
		if (traceabilityClient.getBase(baseId).getData() == null) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "基地不存在"));
		}
		// 获取基地所有邀请记录
		Map<String, LinkedList<String>> invitationRecord = (Map<String, LinkedList<String>>) redisTemplate.opsForHash().get("InvitationRecord", baseId);
		if (invitationRecord == null
				|| invitationRecord.get(typeId) == null
				|| invitationRecord.get(typeId).size() == 0) {
			// 该基地没有邀请过人或没有这种类型的记录
			return null;
		}
		// 获取指定类型的邀请记录，进行查库封装数据
		return userDao.findAllById(invitationRecord.get(typeId));
	}

	/**
	 * 根据老板用户Id获取基地用户
	 *
	 * @param userId
	 * @return
	 */
	public ArrayList<User> getUserByUserId(String userId) {
		try {
			User baseUser = userDao.findByUserId(userId);
			ArrayList<User> users = userDao.getUsersByBaseId(baseUser.getBaseId());
			for (User user : users) {
				user.setPassword(null);
			}
			return users;
		} catch (GlobalException e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, e.getMessage()));
		} catch (Exception e) {
			e.printStackTrace();
			throw new GlobalException(new Result(false, StatusCode.ERROR, "获取用户信息失败"));
		}
	}

	/**
	 * 基地清空所有邀请记录
	 *
	 * @param baseId
	 */
	public void deleteInvitationRecordByBaseId(String baseId) {
		// 查询基地是否存在
		if (traceabilityClient.getBase(baseId).getData() == null) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "删除失败"));
		}
		// 从redis中获取基地的邀请日志
		Map<String, LinkedList<String>> invitationRecord = (Map<String, LinkedList<String>>) redisTemplate.opsForHash().get("InvitationRecord", baseId);
		if (invitationRecord == null) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "删除失败"));
		}
		invitationRecord.put("0", new LinkedList<String>());
		invitationRecord.put("1", new LinkedList<String>());
		invitationRecord.put("2", new LinkedList<String>());
		redisTemplate.opsForHash().put("InvitationRecord", baseId, invitationRecord);
	}


	/**
	 * 基地清空指定类型的邀请记录
	 *
	 * @param baseId
	 */
	public void deleteTypeInvitationRecord(String baseId, String typeId) {
		// 查询基地是否存在
		if (traceabilityClient.getBase(baseId).getData() == null) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "删除失败"));
		}
		// 从redis中获取基地的邀请日志
		Map<String, LinkedList<String>> invitationRecord = (Map<String, LinkedList<String>>) redisTemplate.opsForHash().get("InvitationRecord", baseId);
		if (invitationRecord == null || invitationRecord.get(typeId) == null || invitationRecord.get(typeId).size() == 0) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "删除失败"));
		}
		invitationRecord.put(typeId, new LinkedList<String>());
		redisTemplate.opsForHash().put("InvitationRecord", baseId, invitationRecord);
	}

	/**
	 * 基地清除某一条邀请记录
	 *
	 * @param userId
	 * @param baseId
	 */
	public void deleteInvitationRecordByUserId(String userId, String baseId, String typeId) {
		Map<String, LinkedList<String>> invitationRecord = (Map<String, LinkedList<String>>) redisTemplate.opsForHash().get("InvitationRecord", baseId);
		if (invitationRecord == null) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "删除失败"));
		}
		LinkedList<String> userIdList = invitationRecord.get(typeId);
		boolean flag = userIdList.remove(userId);
		if (!flag) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "删除失败"));
		}
		invitationRecord.put(typeId, userIdList);
		redisTemplate.opsForHash().put("InvitationRecord", baseId, invitationRecord);
	}

	/**
	 * 用户清空所有待确认记录
	 *
	 * @param userId
	 */
	public void deleteAllInvitation(String userId) {
		// 查询用户是否已经有基地了，如果有基地了，那一定没有记录
		if (traceabilityClient.getBase(userId).getData() != null) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "删除失败"));
		}
		// 获取用户所有待确认记录
		LinkedList<String> baseIdList = (LinkedList<String>) redisTemplate.opsForHash().get("InvitationMessage", userId);
		for (String str : baseIdList) {
			Map<String, LinkedList<String>> invitationRecordMap = (Map<String, LinkedList<String>>) redisTemplate.opsForHash().get("InvitationRecord", str);
			invitationRecordMap.get("2").remove(userId);
			invitationRecordMap.get("0").add(userId);
			redisTemplate.opsForHash().put("InvitationRecord", str, invitationRecordMap);
		}
		redisTemplate.opsForHash().delete("InvitationMessage", userId);
	}

	/**
	 * 用户删除指定基地的邀请
	 *
	 * @param userId
	 * @param baseId
	 */
	public void deleteInvitationByBaseId(String userId, String baseId) {
		executeSelection(userId, baseId, "0");
	}

	/**
	 * 修改雇员用户的基地身份
	 *
	 * @param userId
	 * @param baseIdentity
	 * @return
	 */
	@Transactional
	public void updateBaseIdentityOfUser(String userId, Integer baseIdentity) {
		User bose = userDao.findByUserId(SecurityContextUtil.getUserDetails().getUserId());
		User employee = userDao.findByUserId(userId);
		if (employee == null) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "所修改的用户不存在"));
		} else if (bose.getBaseIdentity() != 2 || employee.getBaseIdentity() == 2 || bose.getBaseId() == null || !bose.getBaseId().equals(employee.getBaseId())) {
			throw new GlobalException(new Result(false, StatusCode.ERROR, "修改失败"));
		}
		userDao.setBase(bose.getBaseId(), baseIdentity, userId);
	}

	/**
	 * 解雇员工
	 *
	 * @param userId       要解雇的员工ID
	 * @param baseId       当前基地ID
	 * @param baseIdentity 当前操作删的身份
	 */
	@Transactional
	public void sackEmployeeByUserIdAndBaseId(String userId, String baseId, Integer baseIdentity) {
		User user = userDao.findByUserIdAndBaseId(userId, baseId);
		if (user == null || user.getBaseIdentity() <= baseIdentity) {
			throw new GlobalException(Result.error(StatusCode.ERROR, "解雇失败"));
		}
		user.setBaseId(null);
		user.setBaseIdentity(null);
		userDao.setBaseNullByUserId(userId);
	}

	/**
	 * 分页查询基地用户信息（不包含当前用户的数据）
	 *
	 * @param userId
	 * @param baseId
	 * @return
	 */
	public List<User> getBaseMemberList(String userId, String baseId) {
		List<User> userList = userDao.findOthersByBaseIdAndUserId(baseId, userId);
		userList.stream().forEach(user -> user.setPassword(null));
		return userList;
	}


}